#include "cpu.h"


struct SPI_DRIVER_S
{
	SPI_TypeDef * spix;
    uint32_t rcc;
    GPIO_TypeDef* GPIOx_nss;
	u16 GPIO_Pin_nss;
    GPIO_TypeDef* GPIOx_sck;
	u16 GPIO_Pin_sck;
    GPIO_TypeDef* GPIOx_miso;
	u16 GPIO_Pin_miso;
    GPIO_TypeDef* GPIOx_mosi;
	u16 GPIO_Pin_mosi;

    uint32_t dma_rcc;
    DMA_Channel_TypeDef * tx_channel;
    uint32_t tx_flag_tc;
    DMA_Channel_TypeDef * rx_channel;
    uint32_t rx_flag_tc;


};

#define SPIx 		SPI1
#define SPIx_RCC	RCC_APB2Periph_SPI1


static struct SPI_DRIVER_S spi_driver = {\
    SPI1, RCC_APB2Periph_SPI1,
    GPIOA,GPIO_Pin_4, GPIOA,GPIO_Pin_5, GPIOA,GPIO_Pin_6, GPIOA,GPIO_Pin_7,
    RCC_AHBPeriph_DMA1, DMA1_Channel3, DMA1_FLAG_TC3, DMA1_Channel2, DMA1_FLAG_TC2
};

/*********************************************************************
 * @fn      SPI_Flash_Init
 *
 * @brief   Configuring the SPI for operation flash.
 *
 * @return  none
 */
void SPI_init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    SPI_InitTypeDef  SPI_InitStructure = {0};

    if(RCC_APB2Periph_SPI1 == spi_driver.rcc)
        RCC_APB2PeriphClockCmd(spi_driver.rcc, ENABLE);
    else
        RCC_APB1PeriphClockCmd( spi_driver.rcc, ENABLE);

    RCC_AHBPeriphClockCmd(spi_driver.dma_rcc, ENABLE);

    GPIO_InitStructure.GPIO_Pin = spi_driver.GPIO_Pin_nss;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(spi_driver.GPIOx_nss, &GPIO_InitStructure);
    GPIO_SetBits(spi_driver.GPIOx_nss, spi_driver.GPIO_Pin_nss);

    GPIO_InitStructure.GPIO_Pin = spi_driver.GPIO_Pin_sck;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(spi_driver.GPIOx_sck, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = spi_driver.GPIO_Pin_miso;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(spi_driver.GPIOx_miso, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = spi_driver.GPIO_Pin_mosi;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(spi_driver.GPIOx_mosi, &GPIO_InitStructure);

    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 7;
    SPI_Init(spi_driver.spix, &SPI_InitStructure);

    SPI_I2S_DMACmd(spi_driver.spix, SPI_I2S_DMAReq_Tx, ENABLE);
    SPI_I2S_DMACmd(spi_driver.spix, SPI_I2S_DMAReq_Rx, ENABLE);

    SPI_Cmd(spi_driver.spix, ENABLE);
}

/*********************************************************************
 * @fn      DMA_Tx_Init
 *
 * @brief   Initializes the DMAy Channelx configuration.
 *
 * @param   DMA_CHx - x can be 1 to 7.
 *          ppadr - Peripheral base address.
 *          memadr - Memory base address.
 *          bufsize - DMA channel buffer size.
 *
 * @return  none
 */
void DMA_Tx_Init(DMA_Channel_TypeDef *DMA_CHx, u32 ppadr, u32 memadr, u16 bufsize)
{
    DMA_InitTypeDef DMA_InitStructure = {0};

    DMA_DeInit(DMA_CHx);

    DMA_InitStructure.DMA_PeripheralBaseAddr = ppadr;
    DMA_InitStructure.DMA_MemoryBaseAddr = memadr;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_BufferSize = bufsize;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA_CHx, &DMA_InitStructure);
}

/*********************************************************************
 * @fn      DMA_Rx_Init
 *
 * @brief   Initializes the SPI1 DMA Channelx configuration.
 *
 * @param   DMA_CHx - x can be 1 to 7.
 *          ppadr - Peripheral base address.
 *          memadr - Memory base address.
 *          bufsize - DMA channel buffer size.
 *
 * @return  none
 */
void DMA_Rx_Init(DMA_Channel_TypeDef *DMA_CHx, u32 ppadr, u32 memadr, u16 bufsize)
{
    DMA_InitTypeDef DMA_InitStructure = {0};

    DMA_DeInit(DMA_CHx);

    DMA_InitStructure.DMA_PeripheralBaseAddr = ppadr;
    DMA_InitStructure.DMA_MemoryBaseAddr = memadr;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = bufsize;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA_CHx, &DMA_InitStructure);
}

void SPI_uninit(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_InitStruct.GPIO_Pin = spi_driver.GPIO_Pin_nss;
    GPIO_Init(spi_driver.GPIOx_nss, &GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin = spi_driver.GPIO_Pin_sck;
    GPIO_Init(spi_driver.GPIOx_sck, &GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin = spi_driver.GPIO_Pin_miso;
    GPIO_Init(spi_driver.GPIOx_miso, &GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin = spi_driver.GPIO_Pin_mosi;
    GPIO_Init(spi_driver.GPIOx_mosi, &GPIO_InitStruct);
}

/*********************************************************************
 * @fn      SPIx_ReadWriteByte
 *
 * @brief   SPIx read or write one byte.
 *
 * @param   TxData - write one byte data.
 *
 * @return  Read one byte data.
 */
u8 SPI_read_write_byte(u8 TxData)
{
    u8 i = 0;
    spi_driver.spix->DATAR = TxData;
    i = 0;
    while ((spi_driver.spix->STATR & SPI_I2S_FLAG_RXNE) == (u16)RESET) {
        i++;
        if (i > 200) {
            return 0;
        }
    }
    return spi_driver.spix->DATAR;
}

u16 SPI_read_bytes(u8 *pBuffer, u32 NumByte)
{
    //u8 i = 0;
    u16 index = 0;

    for (; index < NumByte; index++) {
        spi_driver.spix->DATAR = 0xFF;
        while ((spi_driver.spix->STATR & SPI_I2S_FLAG_RXNE) == (u16)RESET);
        pBuffer[index] = (u8)spi_driver.spix->DATAR;
    }
    return index;
}

u16 SPI_write_bytes(const u8 *pBuffer, u32 NumByte)
{
    //u8 i = 0;
    u16 index = 0;
    for (; index < NumByte; index++) {
        spi_driver.spix->DATAR = pBuffer[index];
        while ((spi_driver.spix->STATR & SPI_I2S_FLAG_RXNE) == (u16)RESET);
        spi_driver.spix->DATAR;
    }
    return index;
}

void SPI_cs_set(u8 H_L)
{
    GPIO_WriteBit(spi_driver.GPIOx_nss, spi_driver.GPIO_Pin_nss, H_L ? Bit_SET : Bit_RESET);
}

void SPI_DMA_read_bytes(u8 *pBuffer, u32 NumByte)
{
    u8 data_tx[1] = {0xFF};

    DMA_Tx_Init(spi_driver.tx_channel, (u32)&spi_driver.spix->DATAR, (u32)(data_tx), NumByte);
    DMA_Rx_Init(spi_driver.rx_channel, (u32)&spi_driver.spix->DATAR, (u32)(pBuffer), NumByte);
    DMA_ClearFlag(spi_driver.tx_flag_tc);
    DMA_ClearFlag(spi_driver.rx_flag_tc);

    DMA_Cmd(spi_driver.rx_channel, ENABLE);
    DMA_Cmd(spi_driver.tx_channel, ENABLE);

    while (!DMA_GetFlagStatus(spi_driver.tx_flag_tc));
    while (!DMA_GetFlagStatus(spi_driver.rx_flag_tc));
    DMA_Cmd(spi_driver.tx_channel, DISABLE);
    DMA_Cmd(spi_driver.rx_channel, DISABLE);
}


